
#include <cassert>
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <omp.h>

#define NDEBUG
#include "matrix.hpp"
using namespace std;
using namespace std::chrono;

// void MyTimer(int type, const char *info = nullptr);

void MyTimer(int type, const char *info = nullptr) {
  static std::chrono::steady_clock::time_point start;
  if (type == 0) {
    start = std::chrono::steady_clock::now();
    printf("Begin %s\n", info);
    return;
  } else {
    auto end = std::chrono::steady_clock::now();
    auto dt =
        std::chrono::duration_cast<std::chrono::duration<double>>(end - start);
    printf("End %s, time taken=%lf\n", info, dt.count());
  }
}
// #define ROW_MULTIPLY_COLUMN

// 行＊列　　串行　1000 1000 1000 8.09s
/**
 * @brief
 *  行＊列　　串行　1000 1000 1000 8.09s
 *          并行  2.245s
 *
 *  缓存友好型   串行  1000 1000 1000 1.39s
 *              并行  0.39s
 * @param argc
 * @param argv
 * @return int
 */
int main(int argc, char *argv[]) {
  if (argc != 5) {
    cerr << "./Matrix.out  N K M num_threads" << endl;
    exit(EXIT_FAILURE);
  }
  const int N = atoi(argv[1]);
  const int K = atoi(argv[2]);
  const int M = atoi(argv[3]);
  const int threads_num = atoi(argv[4]);

  cout << "\n" << N << "*" << K << " multiply " << K << "*" << M << endl;

  Matrix a(N, K), b(K, M), c(N, M);
  a.RandomInitialize();
  b.RandomInitialize();

#ifdef ROW_MULTIPLY_COLUMN
  Matrix c(N, M);
  steady_clock::time_point t1{steady_clock::now()};
#pragma omp parallel for
  for (int i = 0; i < N; ++i) {
    for (int j = 0; j < M; ++j) {
      double temp{0.0};
      for (int k = 0; k < K; ++k)
        temp += a(i, k) * b(k, j);
      c(i, j) = temp;
    }
  }
  auto t2 = steady_clock::now();
  duration<double> dr = duration_cast<duration<double>>(t2 - t1);
  cout << "\ntime = " << dr.count() << endl;
#endif

  c = 0.0;
  steady_clock::time_point t11{steady_clock::now()};
#pragma omp parallel for num_threads(threads_num)
  for (int i = 0; i < N; ++i) {
    for (int j = 0; j < K; ++j) {
      for (int k = 0; k < M; ++k)
        c(i, k) += a(i, j) * b(j, k);
    }
  }
  auto t22 = steady_clock::now();
  duration<double> dr2 = duration_cast<duration<double>>(t22 - t11);
  cout << "\nparalleral computation time = " << dr2.count() << endl;

  // 检查正确性
  Matrix cc(N, M);
  // 串行
  MyTimer(0, "serial computation");
  MatrixMultiply(a, b, cc);
  MyTimer(1, "serial computation");
  if (cc.IsSame(c)) {
    cout << "check passed\n" << endl;
  } else {
    cout << "not equal! there is something wrong!\n";
  }

//   cout << cc.IsSame(c) << endl;
  //   c(2, 2) = 4.0;
  //   cout << cc.IsSame(c) << endl;
  return 0;
}